1.1.3
[adiumx.git] / Other / Adium Spotlight Importer / GetMetadataForFile.m
blobad1b51fd73ad79ab4ffff0942a235d3127cbeb46
1 #include <CoreFoundation/CoreFoundation.h>
2 #include <CoreServices/CoreServices.h> 
3 #import "GetMetadataForHTMLLog.h"
4 #import <AIUtilities/NSCalendarDate+ISO8601Parsing.h>
6 /*
7  Relevant keys from MDItem.h we use or may want to use:
8  
9  @constant kMDItemContentCreationDate
10  This is the date that the contents of the file were created,
11  has an application specific semantic.
12  Type is a CFDate.
14  @constant kMDItemTextContent
15  Contains the text content of the document. Type is a CFString.
17  @constant kMDItemDisplayName
18  This is the localized version of the LaunchServices call
19  LSCopyDisplayNameForURL()/LSCopyDisplayNameForRef().
21  @const  kMDItemInstantMessageAddresses
22  Instant message addresses for this item. Type is an Array of CFStrings.
23  */
25 /* -----------------------------------------------------------------------------
26 Get metadata attributes from file
28 This function's job is to extract useful information your file format supports
29 and return it as a dictionary
30 ----------------------------------------------------------------------------- */
32 Boolean GetMetadataForXMLLog(NSMutableDictionary *attributes, NSString *pathToFile);
33 NSString *GetTextContentForXMLLog(NSString *pathToFile);
35 Boolean GetMetadataForFile(void* thisInterface, 
36                                                    CFMutableDictionaryRef attributes, 
37                                                    CFStringRef contentTypeUTI,
38                                                    CFStringRef pathToFile)
40     /* Pull any available metadata from the file at the specified path */
41     /* Return the attribute keys and attribute values in the dict */
42     /* Return TRUE if successful, FALSE if there was no data provided */
43     
44         Boolean                         success = FALSE;
45         NSAutoreleasePool       *pool;
46         pool = [[NSAutoreleasePool alloc] init];
48         if (CFStringCompare(contentTypeUTI, (CFStringRef)@"com.adiumx.htmllog", kCFCompareBackwards) == kCFCompareEqualTo) {
49                 success = GetMetadataForHTMLLog((NSMutableDictionary *)attributes, (NSString *)pathToFile);
50         } else if (CFStringCompare(contentTypeUTI, (CFStringRef)@"com.adiumx.xmllog", kCFCompareBackwards) == kCFCompareEqualTo) {
51                 success = GetMetadataForXMLLog((NSMutableDictionary *)attributes, (NSString *)pathToFile);
52         } else {
53                 NSLog(@"We were passed %@, of type %@, which is an unknown type",pathToFile,contentTypeUTI);
54         }
56         [pool release];
57         
58     return success;
61 /*!
62  * @brief Copy the text content for a file
63  *
64  * This is the text which would be the kMDItemTextContent for the file in Spotlight.
65  *
66  * @param contentTypeUTI The UTI type. If NULL, the extension of pathToFile will be used
67  * @param pathToFile The full path to the file
68  *
69  * @result The kMDItemTextContent. Follows the Copy rule.
70  */
71 CFStringRef CopyTextContentForFile(CFStringRef contentTypeUTI,
72                                                                    CFStringRef pathToFile)
74         NSAutoreleasePool       *pool;
75         CFStringRef                     textContent;
76         pool = [[NSAutoreleasePool alloc] init];
77         
78         //Deteremine the UTI type if we weren't passed one
79         if (contentTypeUTI == NULL) {
80                 if (CFStringCompare((CFStringRef)[(NSString *)pathToFile pathExtension],
81                                                         CFSTR("chatLog"),
82                                                         (kCFCompareBackwards | kCFCompareCaseInsensitive)) == kCFCompareEqualTo) {
83                         contentTypeUTI = CFSTR("com.adiumx.xmllog");
84                 } else if (CFStringCompare((CFStringRef)[(NSString *)pathToFile pathExtension],
85                                                         CFSTR("AdiumXMLLog"),
86                                                         (kCFCompareBackwards | kCFCompareCaseInsensitive)) == kCFCompareEqualTo) {
87                         contentTypeUTI = CFSTR("com.adiumx.xmllog");
88                 } else {
89                         //Treat all other log extensions as HTML logs (plaintext will come out fine this way, too)
90                         contentTypeUTI = CFSTR("com.adiumx.htmllog");
91                 }
92         }
93                 
94         if (CFStringCompare(contentTypeUTI, CFSTR("com.adiumx.htmllog"), kCFCompareBackwards) == kCFCompareEqualTo) {
95                 textContent = (CFStringRef)GetTextContentForHTMLLog((NSString *)pathToFile);
96         } else if (CFStringCompare(contentTypeUTI, (CFStringRef)@"com.adiumx.xmllog", kCFCompareBackwards) == kCFCompareEqualTo) {
97                 textContent = (CFStringRef)GetTextContentForXMLLog((NSString *)pathToFile);
98                 
99         } else {
100                 textContent = nil;
101                 NSLog(@"We were passed %@, of type %@, which is an unknown type",pathToFile,contentTypeUTI);
102         }
104         if (textContent) CFRetain(textContent);
105         [pool release];
106         
107         return textContent;
111  * @brief get metadata for an XML file
113  * This function gets the metadata contained within a universal chat log format file
114  * @param attributes The dictionary in which to store the metadata
115  * @param pathToFile The path to the file to index
117  * @result true if successful, false if not
118  */
119 Boolean GetMetadataForXMLLog(NSMutableDictionary *attributes, NSString *pathToFile)
121         Boolean ret = YES;
122         NSXMLDocument *xmlDoc;
123         NSError *err=nil;
124         NSURL *furl = [NSURL fileURLWithPath:(NSString *)pathToFile];
125         xmlDoc = [[NSXMLDocument alloc] initWithContentsOfURL:furl
126                                                                                                                                                 options:NSXMLNodePreserveCDATA
127                                                                                                                                                   error:&err];    
128         
129         if (xmlDoc)
130         {        
131                 NSArray *authorsArray = [xmlDoc nodesForXPath:@"//message/@sender"
132                                                                                                 error:&err];
133                 NSSet *duplicatesRemover = [NSSet setWithArray: authorsArray];
134                 authorsArray = [duplicatesRemover allObjects];
135                 
136                 [(NSMutableDictionary *)attributes setObject:authorsArray
137                                                                                           forKey:(NSString *)kMDItemAuthors];
138                 
139                 NSArray *contentArray = [xmlDoc nodesForXPath:@"//message//text()"
140                                                                                                 error:&err];
141                 NSString *contentString = [contentArray componentsJoinedByString:@" "];
142                 
143                 [attributes setObject:contentString
144                                            forKey:(NSString *)kMDItemTextContent];
146                 NSString *serviceString = [[[xmlDoc rootElement] attributeForName:@"service"] objectValue];
147                 if(serviceString != nil)
148                         [attributes setObject:serviceString
149                                                    forKey:@"com_adiumX_service"];
150                 
151                 NSArray                 *children = [[xmlDoc rootElement] children];
152                 NSCalendarDate  *startDate = nil, *endDate = nil;
154                 if ([children count]) {
155                         NSString                *dateStr;
157                         dateStr = [[(NSXMLElement *)[children objectAtIndex:0] attributeForName:@"time"] objectValue];
158                         startDate = (dateStr ? [NSCalendarDate calendarDateWithString:dateStr] : nil);
159                         if (startDate)
160                                 [(NSMutableDictionary *)attributes setObject:startDate
161                                                                                                           forKey:(NSString *)kMDItemContentCreationDate];
163                         dateStr = [[(NSXMLElement *)[children objectAtIndex:0] attributeForName:@"time"] objectValue];
164                         endDate = (dateStr ? [NSCalendarDate calendarDateWithString:dateStr] : nil);
165                         if (endDate)
166                                 [(NSMutableDictionary *)attributes setObject:[NSNumber numberWithInt:[endDate timeIntervalSinceDate:startDate]]
167                                                                                                           forKey:(NSString *)kMDItemDurationSeconds];
168                 }
170                 NSString *accountString = [[[xmlDoc rootElement] attributeForName:@"account"] objectValue];
171                 if (accountString) {
172                         [attributes setObject:accountString
173                                                    forKey:@"com_adiumX_chatSource"];
174                         NSMutableArray *otherAuthors = [authorsArray mutableCopy];
175                         [otherAuthors removeObject:accountString];
176                         //pick the first author for this.  likely a bad idea
177                         if (startDate && [otherAuthors count]) {
178                                 NSString *toUID = [otherAuthors objectAtIndex:0];
179                                 [attributes setObject:[NSString stringWithFormat:@"%@ on %@",toUID,[startDate descriptionWithCalendarFormat:@"%y-%m-%d"
180                                                                                                                                                                                                                                    timeZone:nil
181                                                                                                                                                                                                                                          locale:nil]]
182                                                            forKey:(NSString *)kMDItemDisplayName];
183                         }
184                         [otherAuthors release];
185                         
186                 }
187                 [attributes setObject:@"Chat log"
188                                            forKey:(NSString *)kMDItemKind];
189                 
190                 [xmlDoc release];
191         }
192         else
193                 ret = NO;
194         
195         return ret;
198 NSString *killXMLTags(NSString *inString)
200     NSScanner *scan = [NSScanner scannerWithString:inString];
201     NSString *tempString = nil;
202     NSMutableString *ret = [NSMutableString string];
203     NSRange rng;
204     
205     while(![scan isAtEnd]){
206         tempString = nil;
207         [scan scanUpToString:@"<" intoString:&tempString];
208         if(tempString != nil)
209             [ret appendFormat:@"%@ ", tempString];
210         [scan scanString:@"<" intoString:nil];
211         [scan scanUpToString:@">" intoString:&tempString];
212         if([tempString hasPrefix:@"br"])
213             [ret appendString:@"\n"];
214         [scan scanString:@">" intoString:nil];
215     }
216     rng.location = -1;
217     do {
218         NSRange searchRange = NSMakeRange(rng.location+1, [ret length]-rng.location-1);
219         rng = [ret rangeOfString:@"&lt;" options:0 range:searchRange];
220         if (rng.length > 0)
221             [ret replaceCharactersInRange: rng withString: @"<"];
222     } while (rng.length > 0);
223     rng.location = -1;
224     do {
225         NSRange searchRange = NSMakeRange(rng.location+1, [ret length]-rng.location-1);
226         rng = [ret rangeOfString:@"&gt;" options:0 range:searchRange];
227         if (rng.length > 0)
228             [ret replaceCharactersInRange: rng withString: @">"];
229     } while (rng.length > 0);
230     rng.location = -1;
231     do {
232         NSRange searchRange = NSMakeRange(rng.location+1, [ret length]-rng.location-1);
233         rng = [ret rangeOfString:@"&amp;" options:0 range:searchRange];
234         if (rng.length > 0)
235             [ret replaceCharactersInRange: rng withString: @"&"];
236     } while (rng.length > 0);
237     return ret;
240 NSString *GetTextContentForXMLLog(NSString *pathToFile)
242         NSError *err=nil;
243         NSURL *furl = [NSURL fileURLWithPath:(NSString *)pathToFile];
244         NSString *contentString;
245         NSXMLDocument *xmlDoc;
246         xmlDoc = [[NSXMLDocument alloc] initWithContentsOfURL:furl
247                                                                                                   options:NSXMLNodePreserveCDATA
248                                                                                                         error:&err];    
249         NSArray *contentArray = [xmlDoc nodesForXPath:@"//message//text()"
250                                                                                         error:&err];
251         contentString = [contentArray componentsJoinedByString:@" "];
252         [xmlDoc release];
253         return contentString;